//------------------------------------------------------------------------------
// prbs_gen_chk.sv
// Konstantin Pavlov, pavlovconst@gmail.com
//------------------------------------------------------------------------------

// INFO ------------------------------------------------------------------------
// This module generates or checks a PRBS pattern
// See "xapp884" appnote for more info

// Set paramaters for compliance to ITU-T Recommendation O.150 Section 5
//--------------------------------- ------------------------------------------
// POLY_LEN   POLY_TAP  INV_PATTERN | nbr of   bit seq.   max 0   feedback
//                                  | stages    length  sequence   stages
//--------------------------------- ------------------------------------------
// 7(non standard)  6        false  |    7         127      6       6, 7
//     9            5        false  |    9         511      8       5, 9
//    11            9        false  |   11        2047     10       9,11
//    15           14        true   |   15       32767     15      14,15
//    20            3        false  |   20     1048575     19       3,20
//    23           18        true   |   23     8388607     23      18,23
//    29           27        true   |   29   536870911     29      27,29
//    31           28        true   |   31  2147483647     31      28,31


/* --- INSTANTIATION TEMPLATE BEGIN ---

prbs_gen_chk #(
  .WIDTH( 32 ),
  .CHK_MODE( 0 ),
  .INV_PATTERN( 1 ),
  .POLY_LEN( 31 ),
  .POLY_TAP( 28 )
) prbs1 (
  .clk( clk ),
  .nrst( nrst ),
  .en( 1'b1 )
  .data_in( 0 ),
  .data_out( d[31:0] )
)

--- INSTANTIATION TEMPLATE END ---*/


module prbs_gen_chk #( parameter
  WIDTH = 32,         // data_in, data_out port width
  CHK_MODE = 0,       // 0 - module is a gen
                      // 1 - module is a chk
  INV_PATTERN = 1,    // invert PRBS bit-wise
  POLY_LEN = 31,      // generator polynomial length
  POLY_TAP = 28       // generator polynomial tap
)(
  input clk,
  input nrst,
  input en,
  input [(WIDTH-1):0] data_in,     // CHK_MODE: data to be checked
                                   // ~CHK_MODE: inject error
  output logic [(WIDTH-1):0] data_out = {WIDTH{1'b1}}
          // CHK_MODE: error found (checker), LSB is the oldest received bit
          // ~CHK_MODE: generated prbs pattern, LSB is the oldest-generated bit
);

 // considering inversion
logic [(WIDTH-1):0] data_in_i;
assign data_in_i[(WIDTH-1):0] = (INV_PATTERN)?
                                (~data_in[(WIDTH-1):0]):
                                (data_in[(WIDTH-1):0]);

logic [(POLY_LEN-1):0] prbs_register = {POLY_LEN{1'b1}};  // LFSR itself
logic [WIDTH:0][(POLY_LEN-1):0] shift_table;
logic [(WIDTH-1):0] xor_results;

// generating [POLY_LEN cols]x[WIDTH+1 rows] table by shifting prbs register
genvar i;
generate
  assign shift_table[0] = prbs_register[(POLY_LEN-1):0];
  for (i=0; i<WIDTH; i=i+1) begin : gen1
    assign xor_results[i] = shift_table[i][POLY_LEN-POLY_TAP] ^ shift_table[i][0];
    assign shift_table[i+1] = { (CHK_MODE)?(data_in_i[i]):(xor_results[i]),
                                 shift_table[i][(POLY_LEN-1):1] };
  end
endgenerate

always_ff @(posedge clk) begin
  if(~nrst) begin
    data_out[(WIDTH-1):0] <= {WIDTH{1'b1}};
    prbs_register[(POLY_LEN-1):0] <= {POLY_LEN{1'b1}};
  end else if (en) begin
    // taking new bits generated by xor operation
    data_out[(WIDTH-1):0] <= xor_results[(WIDTH-1):0] ^ data_in_i[(WIDTH-1):0];
    // storing prbs register
    prbs_register[(POLY_LEN-1):0] <= shift_table[WIDTH][(POLY_LEN-1):0];
  end
end

endmodule

